using System.Collections.Generic;
using UnityEngine;

namespace UFrame
{
    /// <summary>
    /// 代表一个对象池
    /// </summary>
    public interface IPool
    {
        void Clear();

        int HasCount();
    }

    /// <summary>
    /// 池接口
    /// </summary>
    public interface IPoolObjectBase
    {
        void PoolInitObject();
        void PoolUnInitObject();
    }

    /// <summary>
    /// 普通对象池
    /// </summary>
    public class Pool<T> : IPool where T : class
    {
        protected Stack<T> data = new Stack<T>();

        public virtual T Spawn()
        {
            T t = null;

            if (data.Count > 0)
            {
                t = data.Pop();
            }

            return t;
        }

        public virtual void Recycle(T t)
        {
            if (t != null && !data.Contains(t))
            {
                data.Push(t);
            }
        }

        public virtual void Clear()
        {
            data.Clear();
        }

        public virtual int HasCount()
        {
            return data.Count;
        }
    }

    /// <summary>
    /// GameObject类型的对象池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GameObjectPool<T> : Pool<T> where T : Component
    {
        public GameObject root;
        public T duplicate;

        public void SetRoot(string name)
        {
            root.name = name;
        }

        public GameObjectPool() : base()
        {
            root = new GameObject();
            root.name = typeof(T).ToString();
            GameObject.DontDestroyOnLoad(root);
        }

        public override T Spawn()
        {
            T t = null;

            if (data.Count > 0)
            {
                t = data.Pop();
                if (t is IPoolObjectBase)
                {
                    ((IPoolObjectBase) t).PoolInitObject();
                }
            }
            else
            {
                t = GameObject.Instantiate(duplicate);
            }

            return t;
        }

        public override void Recycle(T t)
        {
            if (t != null && !data.Contains(t))
            {
                if (t is IPoolObjectBase)
                {
                    ((IPoolObjectBase) t).PoolUnInitObject();
                }

                if (root != null)
                {
                    t.gameObject.SetActive(false);
                    if (t.transform.parent != root.transform)
                    {
                        root.transform.SetParent(root.transform);
                        //child.transform.localPosition = Vector3.zero;
                        //child.transform.localScale = Vector3.one;
                        //child.transform.localRotation = Quaternion.identity;
                    }

                    data.Push(t);
                }
                else
                {
                    GameObject.Destroy(t);
                }
            }
        }

        public override void Clear()
        {
            foreach (T t in data)
            {
                if (t is IPoolObjectBase)
                {
                    ((IPoolObjectBase) t).PoolUnInitObject();
                }
            }

            duplicate = null;
            GameObject.Destroy(root);
            data.Clear();
        }
    }


    /// <summary>
    /// 对象池统一使用的接口
    /// </summary>
    public static class PoolMa
    {
        private static Dictionary<string, IPool> dic = new Dictionary<string, IPool>();

        public static void Clear()
        {
            foreach (KeyValuePair<string, IPool> value in dic)
            {
                value.Value.Clear();
            }

            dic.Clear();
        }

        public static int HasCount(string name)
        {
            IPool p = null;
            if (dic.TryGetValue(name, out p))
            {
                return p.HasCount();
            }

            return 0;
        }


        public static void RecycleObj(string name, GameObject t)
        {
            RecycleObj(name, t.GetComponent<Transform>());
        }

        public static void RecycleObj<T>(string name, T t) where T : Component
        {
            GameObjectPool<T> pool = null;

            IPool p = null;
            if (dic.TryGetValue(name, out p))
            {
                pool = p as GameObjectPool<T>;
            }
            else
            {
                pool = new GameObjectPool<T>();
                dic.Add(name, pool);
            }

            if (t.gameObject.activeInHierarchy)
                t.gameObject.SetActive(false);
            pool.Recycle(t);
        }


        public static void PrepareObj(string name, GameObject oc)
        {
            PrepareObj(name, oc.GetComponent<Transform>());
        }

        /// <summary>
        /// 预先存储标本,然后调用Spawn生成对象
        /// </summary>
        public static void PrepareObj<T>(string name, T oc) where T : Component
        {
            GameObjectPool<T> p = null;
            if (dic.ContainsKey(name))
            {
                p = dic[name] as GameObjectPool<T>;
            }
            else
            {
                p = new GameObjectPool<T>();
                dic.Add(name, p);
            }

            p.SetRoot(name + "Pool");
            p.duplicate = oc;
        }

        public static GameObject SpawnObj(string name)
        {
            return SpawnObj<Transform>(name).gameObject;
        }

        public static T SpawnObj<T>(string name) where T : Component
        {
            GameObjectPool<T> p = null;
            if (dic.ContainsKey(name))
            {
                var po = dic[name];
                p = po as GameObjectPool<T>;
                return p.Spawn();
            }

            return null;
        }
    }
}